// epoll_server.hpp封装epoll服务器
#pragma once
#include "Socket.hpp"
#include <sys/epoll.h>

#define BACK_LOG 5
#define SIZE 256//设置的创建的模型大小
#define MAX_NUM 64//表示最多返回的事件的数量
class epollServer
{
public:
epollServer(int port)
        : _port(port)
    {}
    void InitepollServer() // 初始化
    {
        _LisSocFd = Socket::SocCreate();
        Socket::SocketBind(_LisSocFd, _port);
        Socket::SocketListen(_LisSocFd, BACK_LOG);

        _epfd=epoll_create(SIZE);//创建epoll模型
        if(_epfd<0)
        {
            std::cerr<<"epoll_create error!"<<std::endl;
            exit(5);
        }
    }
    ~epollServer()
    {
        if (_LisSocFd >= 0)
        {
            close(_LisSocFd);
        }
        if(_epfd>=0)
        {
            close(_epfd);
        }
    }

    void HandlerEvent(struct epoll_event revs[],int num)
    {
        for(int i=0;i<num;i++)
        {
            int fd=revs[i].data.fd;
            if(fd==_LisSocFd&&revs[i].events&EPOLLIN)
            {
                struct sockaddr_in peer;
				memset(&peer, 0, sizeof(peer));
				socklen_t len = sizeof(peer);
                int sockFd=accept(_LisSocFd,(struct sockaddr*)&peer,&len);
                if(sockFd<0)
                {
                    std::cerr<<"accept error!"<<std::endl;
                    continue;;
                }
                std::string peer_ip = inet_ntoa(peer.sin_addr);
				int peer_port = ntohs(peer.sin_port);
				std::cout << "get a new link[" << peer_ip << ":" << peer_port << "]" << std::endl;
                AddEvent(sockFd, EPOLLIN);//将取到的套接字添加到epoll模型中，并关心其读事件
            }
            else if(revs[i].events&EPOLLIN)//读事件就绪
            {
                char buffer[64];
                ssize_t s=recv(fd,buffer,sizeof(buffer)-1,0);
                if(s>0)//读取成功
                {
                    buffer[s]='\0';
                    std::cout<<"echo#"<<buffer<<std::endl;
                }
                else if(s==0)//对端连接关闭
                {
                    std::cout<<"Client quit"<<std::endl;
                    close(fd);
                    DelEvent(fd);//将文件描述符从模型中删除
                }
                else{//读取错误
                    std::cerr<<"recv error!"<<std::endl;
                    close(fd);
                    DelEvent(fd);
                }
            }

        }
    }
    
    void Run()
    {
        AddEvent(_LisSocFd, EPOLLIN);//添加监听套接字到epoll模型中，并关系它的读事件
        for(;;)
        {
            struct epoll_event revs[MAX_NUM];//定义一个数组，用于存储epoll_wait返回的就绪事件表
            int num=epoll_wait(_epfd,revs,MAX_NUM,-1);//等待事件的发生，-1表示永久阻塞直至至少一个事件的发生
            if(num<0)
            {
                std::cerr<<"epoll_wait error"<<std::endl;//发生错误
                continue;
            }
            else if(num==0)
            {
                std::cout<<"timeout..."<<std::endl;//调用超时
                continue;
            }
            else{
                HandlerEvent(revs,num);
            }
        }
    }

private:
   void AddEvent(int sockFd,uint32_t event)//添加文件描述符和事件到epoll模型中
   {
    struct epoll_event ev;//创建结构体，将文件描述符和想要被关注的事件添加到结构体中
    ev.events=event;
    ev.data.fd=sockFd;

    epoll_ctl(_epfd,EPOLL_CTL_ADD,sockFd,&ev);//添加该结构体到模型中
   }
   void DelEvent(int sockFd)//从epoll模型中删除文件描述符，不关心事件，所以事件设为nullptr
   {
    epoll_ctl(_epfd,EPOLL_CTL_DEL,sockFd,nullptr);
   }
private:
    int _port;//端口号
    int _LisSocFd;//监听套接字
    int _epfd;//epoll模型
};